/*
 *  node.cc
 *  wonderland-project
 *
 *  Created by Ben Nolan on 8/10/08.
 *  Copyright 2008 Nolan Consulting Limited. All rights reserved.
 *
 */

#include <v8.h>
#include <Ogre.h>
#include "node.h"
#include "vector.h"
#include "quaternion.h"
#include "entity.h"
#include "v8defines.h"
#include "scripting_context.h"
#include "region.h"
#include "sync_manager.h"

namespace JSNode{
  
	v8::Handle<v8::ObjectTemplate> instance_template;
	v8::Handle<v8::FunctionTemplate> function_template;
	v8::Persistent<v8::Context> context_;

	Wonderland::Region *region_;

  V8_GETTER(Node, id){
    return v8::String::New(V8INTERNAL(Ogre::SceneNode*)->getName().c_str());
  }

  V8_GETTER(Node, entity){
    return JSEntity::New((Ogre::Entity*) V8INTERNAL(Ogre::SceneNode*)->getAttachedObject(0));
  }

  V8CALLBACK(Node, appendChild){
    if (!V8ARGUMENTTYPE("Entity", 0))
      return ThrowException(v8::String::New("Invalid argument"));

    V8INTERNAL(Ogre::SceneNode*)->detachObject((unsigned short) 0);
    V8INTERNAL(Ogre::SceneNode*)->attachObject(V8ARGUMENT(Ogre::Entity*, 0));

    return v8::Undefined();
  }

  V8CALLBACK(Node,destroy){ 
	V8INTERNAL(Ogre::SceneNode*)->getCreator()->destroySceneNode(V8INTERNAL(Ogre::SceneNode*));
    return v8::Undefined();
  }

  V8CALLBACK(Node,getRotation){ 
    return JSQuaternion::New(V8INTERNAL(Ogre::SceneNode*)->getOrientation());
  }

  V8CALLBACK(Node,setRotation){ 
    if (!V8ARGUMENTTYPE("Quaternion", 0))
      return ThrowException(v8::String::New("Invalid argument"));

    V8INTERNAL(Ogre::SceneNode*)->setOrientation(*V8ARGUMENT(Ogre::Quaternion*, 0));
//	region_->getSyncManager()->addNode(V8INTERNAL(Ogre::SceneNode*));

    return v8::Undefined();
  }

  V8CALLBACK(Node,getPosition){ 
    return JSVector::New(V8INTERNAL(Ogre::SceneNode*)->getPosition());
  }

  V8CALLBACK(Node,setPosition){ 
    if (!V8ARGUMENTTYPE("Vector", 0))
      return ThrowException(v8::String::New("Invalid argument"));

    V8INTERNAL(Ogre::SceneNode*)->setPosition(*V8ARGUMENT(Ogre::Vector3*, 0));
//	region_->getSyncManager()->addNode(V8INTERNAL(Ogre::SceneNode*));
    
    return v8::Undefined();
  }

  V8CALLBACK(Node,getScale){ 
    return JSVector::New(V8INTERNAL(Ogre::SceneNode*)->getScale());
  }

  V8CALLBACK(Node,setScale){ 
    if (!V8ARGUMENTTYPE("Vector", 0))
      return ThrowException(v8::String::New("Invalid argument"));

    V8INTERNAL(Ogre::SceneNode*)->setScale(*V8ARGUMENT(Ogre::Vector3*, 0));
//	region_->getSyncManager()->addNode(V8INTERNAL(Ogre::SceneNode*));
    
    return v8::Undefined();
  }

  /* Construct a new node - doesnt actually allocate the node, use world.createNode for that */
  V8CALLBACK(Node,create){
    v8::Persistent<v8::Object> self = v8::Persistent<v8::Object>::New(args.Holder());
    return self;
  }

  /* Sets internal fields with our idiom of storing the class name in the second field */
  void setInternalFields(v8::Persistent<v8::Object> p, Ogre::SceneNode *n){
    p->SetInternalField(0, v8::External::New(n));
    p->SetInternalField(1, v8::String::New("Node"));
  }

  /* Only called from c++ */
  v8::Handle<v8::Value> New(Ogre::SceneNode *node){
    context_->Enter();

    v8::Persistent<v8::Object> instance = v8::Persistent<v8::Object>::New(function_template->GetFunction()->NewInstance());
    setInternalFields(instance, node);
    return instance;
  }

  void Initialize(Wonderland::ScriptingContext *sc, Wonderland::Region *region){
    context_ = sc->getContext();
	region_ = region;

    // Function Template
    function_template = v8::FunctionTemplate::New(Node_create);
    function_template->SetClassName(v8::String::New("Node"));

    // Instance template
    instance_template = function_template->InstanceTemplate();
    instance_template->SetInternalFieldCount(2);

    #define V8REGISTER(function) instance_template->Set(#function, v8::FunctionTemplate::New(Node_ ## function));
    
    instance_template->SetAccessor(v8::String::New("id"), Node_getid);
    instance_template->SetAccessor(v8::String::New("entity"), Node_getentity);

    V8REGISTER(appendChild);

    V8REGISTER(destroy);
    V8REGISTER(getRotation);
    V8REGISTER(setRotation);
    V8REGISTER(getPosition);
    V8REGISTER(setPosition);
    V8REGISTER(getScale);
    V8REGISTER(setScale);
  }

}

